package com.shengqin.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shengqin.common.pojo.CommonPage;
import com.shengqin.config.CommonException;
import com.shengqin.entity.*;
import com.shengqin.mapper.StudentMapper;
import com.shengqin.pojo.student.StudentImportParam;
import com.shengqin.service.IClassesService;
import com.shengqin.service.ISchoolService;
import com.shengqin.service.IStudentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shengqin.service.IUserService;
import com.shengqin.utils.GradeUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生信息 服务实现类
 * </p>
 *
 * @author Rui.Zhou
 * @since 2023-12-05
 */
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");

    @Resource
    private IClassesService classesService;
    @Resource
    private ISchoolService schoolService;
    @Resource
    private IUserService userService;

    @Override
    public List<Student> listByClassesId(Integer classesId) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getClassesId, classesId);
        return list(wrapper);
    }

    @Override
    public List<Student> listByClassesIds(Collection<Integer> classesIds) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
//        wrapper.select("id,study_code,classes_id,school_id,grade_id,sex");
        wrapper.lambda().in(Student::getClassesId, classesIds);
        return list(wrapper);
    }

    @Override
    public Page<Student> getPage(CommonPage<Student> pageParam) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        Student studentParam = pageParam.getQueryParam();
        if (StringUtils.hasLength(studentParam.getName())) {
            wrapper.lambda().like(Student::getName, studentParam.getName());
        }
        if (StringUtils.hasLength(studentParam.getStudyCode())) {
            wrapper.lambda().eq(Student::getStudyCode, studentParam.getStudyCode());
        }
        if (studentParam.getGradeId() != null) {
            wrapper.lambda().eq(Student::getGradeId, studentParam.getGradeId());
        }
        if (StringUtils.hasLength(studentParam.getSex())) {
            wrapper.lambda().eq(Student::getSex, studentParam.getSex());
        }
        if (StringUtils.hasLength(studentParam.getIdNumber())) {
            wrapper.lambda().eq(Student::getIdNumber, studentParam.getIdNumber());
        }
        if (StringUtils.hasLength(studentParam.getCardNo())) {
            wrapper.lambda().eq(Student::getCardNo, studentParam.getCardNo());
        }
        if (studentParam.getClassesId() != null) {
            wrapper.lambda().eq(Student::getClassesId, studentParam.getClassesId());
        }
        if (studentParam.getSchoolId() != null) {
            wrapper.lambda().eq(Student::getSchoolId, studentParam.getSchoolId());
        }
        if (studentParam.getGraduateFlag() != null) {
            wrapper.lambda().eq(Student::getGraduateFlag, studentParam.getGraduateFlag());
        }

        if (!StpUtil.getLoginId().equals("1")) {
            User user = userService.getById(StpUtil.getLoginId().toString());
            List<School> schools = schoolService.listByUserId(user.getId());
            if (CollectionUtils.isEmpty(schools)) {
                return new Page<>();
            }
            wrapper.lambda().in(Student::getSchoolId, schools.stream().map(School::getId).collect(Collectors.toSet()));
        }
        List<String> orders = new ArrayList<>();
        orders.add("grade_id");
        orders.add("classes_name");
        orders.add("study_code");
        wrapper.orderByAsc(orders);
        Page<Student> studentPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
        page(studentPage, wrapper);
        buildRecords(studentPage.getRecords());
        return studentPage;
    }

    @Override
    public Student getByStudyCode(String studyCode) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getStudyCode, studyCode);
        return getOne(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean importBatch(List<StudentImportParam> studentImportParams) {

        try {

            if (CollectionUtils.isEmpty(studentImportParams)) {
                return true;
            }
            Set<String> errorList = new HashSet<>();
            Map<String,List<StudentImportParam>> studentImportParamMap = studentImportParams.stream().collect(Collectors.groupingBy(StudentImportParam::getStudyCode));
            List<Student> bizStudents = new ArrayList<>();
            List<School> hasedSchoolList = schoolService.listByUserId(Integer.valueOf(StpUtil.getLoginIdAsString()));
            studentImportParamMap.forEach((studyCode,studentParams) -> {
                StudentImportParam studentImportParam = studentParams.get(0);
                String schoolName = studentImportParam.getSchoolName().trim();
                Student bizStudent = new Student();
                BeanUtils.copyProperties(studentImportParam, bizStudent);
                if(!StringUtils.hasLength(studentImportParam.getClassesCode())){
                    errorList.add("班级编码【" + studentImportParam.getSchoolName() + "】不能有空值");
                }
                if (!StringUtils.hasLength(schoolName)) {
                    errorList.add("学校名称【" + schoolName + "】存在空行");
                } else {
                    School school = hasedSchoolList.stream().filter(s -> s.getName().trim().equals(schoolName.trim())).findFirst().orElse(null);
                    if (school == null) {
                        errorList.add("学校名称【" + schoolName + "】不存在");
                    } else {
                        bizStudent.setSchoolId(school.getId());
                    }
                }
                bizStudent.setGradeId(Integer.valueOf(studentImportParam.getGradeId()));
                if (StringUtils.hasLength(studentImportParam.getBirthday())) {
                    bizStudent.setBirthday(LocalDate.parse(studentImportParam.getBirthday(), dateTimeFormatter));
                }
                bizStudent.setCreateTime(LocalDateTime.now());
                if (!StringUtils.hasLength(bizStudent.getClassesName())) {
                    bizStudent.setClassesName(bizStudent.getClassesCode());
                }
                bizStudents.add(bizStudent);
            });

            if (!errorList.isEmpty()) {
                throw new CommonException(String.join(", ", errorList));
            }

            Map<Integer,List<Student>> studentMap = bizStudents.stream().collect(Collectors.groupingBy(Student::getSchoolId));
            studentMap.forEach((schoolId,students) -> {
                List<Student> bizStudentList = new ArrayList<>();
                Set<String> classesCodes = new HashSet<>();
                List<String> studyCodes = new ArrayList<>();
                Map<String, String> classesCodeGradeMap = new HashMap<>();
                Map<String, String> classesCodeNameMap = new HashMap<>();
                students.forEach(student -> {
                    studyCodes.add(student.getStudyCode());
                    classesCodes.add(student.getClassesCode());
                    classesCodeGradeMap.put(student.getClassesCode(), student.getGradeId().toString());
                    classesCodeNameMap.put(student.getClassesCode(), student.getClassesName());
                });
                List<Classes> classesList;
                Map<String, Classes> classesMap = new HashMap<>();
                if (!CollectionUtils.isEmpty(classesCodes)) {
                    classesList = classesService.listByCodes(classesCodes, schoolId);
                    classesMap = classesList.stream().collect(Collectors.toMap(Classes::getCode, c -> c));
                    Map<String, Classes> finalClassesMap = classesMap;
                    List<Classes> insertBizClasses = new ArrayList<>();
                    classesCodes.forEach(classesCode -> {
                        if (!finalClassesMap.containsKey(classesCode)) {
                            Classes classes = new Classes();
                            classes.setCode(classesCode);
                            classes.setSchoolId(schoolId);
                            classes.setGradeId(Integer.valueOf(classesCodeGradeMap.get(classesCode)));
                            classes.setName(classesCodeNameMap.get(classesCode));
                            classes.setCreateTime(LocalDateTime.now());
                            insertBizClasses.add(classes);
                        }
                    });
                    if (!CollectionUtils.isEmpty(insertBizClasses)) {
                        classesService.saveBatch(insertBizClasses);
                        Map<String, Classes> finalClassesFilterMap = classesMap;
                        insertBizClasses.forEach(bizClasses -> {
                            finalClassesFilterMap.put(bizClasses.getCode(), bizClasses);
                        });
                    }
                }
                List<Student> waitUpdateList;
                Map<String, Student> waitUpdateStudentMap = new HashMap<>();
                if (!CollectionUtils.isEmpty(studyCodes)) {
                    waitUpdateList = queryByStudyCodes(studyCodes);
                    waitUpdateStudentMap = waitUpdateList.stream().collect(Collectors.toMap(Student::getStudyCode, s -> s));
                }

                Map<String, Student> finalWaitUpdateStudentMap = waitUpdateStudentMap;
                students.forEach(bizStudent -> {
                    Student addStudent = finalWaitUpdateStudentMap.getOrDefault(bizStudent.getStudyCode(), null);
                    if (addStudent == null) {
                        bizStudentList.add(bizStudent);
                    } else {
                        Integer stdId = addStudent.getId();
                        BeanUtils.copyProperties(bizStudent,addStudent);
                        addStudent.setId(stdId);
                        bizStudentList.add(addStudent);
                    }
                });

                Map<String, Classes> finalClassesMap = classesMap;
                bizStudentList.forEach(student -> {
                    Classes classes = finalClassesMap.getOrDefault(student.getClassesCode(), null);
                    student.setSchoolId(classes.getSchoolId());
                    student.setClassesId(classes.getId());
                    student.setClassesName(classes.getName());
                });

                saveOrUpdateBatch(bizStudentList);

            });

        } catch (Exception e) {
            log.error("导入学生数据报错", e);
            throw new CommonException(e.getMessage());
        }

        return true;
    }

    @Override
    public List<Student> queryByStudyCodes(Collection<String> studyCodes) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(Student::getStudyCode, studyCodes);
        return list(wrapper);
    }

    @Override
    public List<Student> listBySchoolId(Integer schoolId) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getSchoolId, schoolId);
        return list(wrapper);
    }

    @Override
    public List<Student> listBySchoolIdAndExcludeStudyCodes(Integer schoolId, Set<String> includeStudyCodes, Set<String> excludeStudyCodes) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getSchoolId, schoolId);
        if (!CollectionUtils.isEmpty(includeStudyCodes)) {
            wrapper.lambda().in(Student::getStudyCode, includeStudyCodes);
        }
        if (!CollectionUtils.isEmpty(excludeStudyCodes)) {
            wrapper.lambda().notIn(Student::getStudyCode, excludeStudyCodes);
        }
        return list(wrapper);
    }

    @Override
    public void removeBySchoolId(Integer schoolId) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getSchoolId, schoolId);
        remove(wrapper);
    }

    @Override
    public Student getByIdNumber(String idCardNo) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getIdNumber, idCardNo);
        List<Student> students = list(wrapper);
        return CollectionUtils.isEmpty(students) ? null : students.get(0);
    }

    @Override
    public Student getByIdNumberAndName(String idCard, String name) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getIdNumber, idCard);
        wrapper.lambda().eq(Student::getName, name);
        List<Student> students = list(wrapper);
        return CollectionUtils.isEmpty(students) ? null : students.get(0);
    }

    @Override
    public List<Student> listBySchoolIds(Set<Integer> schoolIds) {
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(Student::getSchoolId, schoolIds);
        return list(wrapper);
    }

    private void buildRecords(List<Student> records) {
        if (!CollectionUtils.isEmpty(records)) {
            Set<Integer> schoolIds = new HashSet<>();
            records.forEach(rec -> {
                schoolIds.add(rec.getSchoolId());
            });
            List<School> schoolList = schoolService.listByIds(schoolIds);
            Map<Integer, School> schoolMap = schoolList.stream().collect(Collectors.toMap(School::getId, s -> s));
            records.forEach(rec -> {
                rec.setSchoolName(schoolMap.getOrDefault(rec.getSchoolId(),new School()).getName());
                rec.setGradeName(GradeUtil.getGradeName(rec.getGradeId()));
            });
        }
    }
}
